/*
 * Tic-Tac-Toe - Turn-based strategy game
 * Copyright 2008, 2009 Shayne Riley and Paul Maseberg
 *
 * Tic-Tac-Toe is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tic-Tac-Toe is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tic-Tac-Toe.  If not, see <http://www.gnu.org/licenses/>
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * To contact the authors on questions or issues regarding the license,
 * you may send an email to <shayner at gmail dot com>
 */

#include "GameBoard.h"
#include <time.h>
#include <stdlib.h>

GameBoard::GameBoard(Difficulty level, Marker humanPlayer) :
    mLevel(level), mHumanPlayer(humanPlayer), mWinner(0)
{
    srand((unsigned)time(NULL));

    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            mGameBoard[i][j] = GameBoard::UNSET;
}

GameBoard::~GameBoard()
{
}

GameBoard::Marker GameBoard::getMarker(int row, int column)
{
    return mGameBoard[row][column];
}

bool GameBoard::makeMove(int row, int column)
{
    bool result(false);
    if (row > -1 && row < 3)
    {
        if (column > -1 && row < 3)
        {
            if (mGameBoard[row][column] == UNSET)
            {
                mGameBoard[row][column] = mHumanPlayer;
                result = true;
            }
        }
    }
    return result;
}

bool GameBoard::makeMove()
{
    if(mLevel == EASY)
        randomAI();
    else if(mLevel == HARD)
        hardAI();

    return true;
}

void GameBoard::hardAI()
{
    // Win: If you have two in a row, play the third to get three in a row.
    if (threeInARow(O))
        return;
    // Block: If the opponent has two in a row, play the third to block them.
    if (threeInARow(X))
        return;

    // Block Opponent's Fork:
    if(mGameBoard[0][0] == X && mGameBoard[2][2] == X)
    {
        // play edge
        if(mGameBoard[0][1] == UNSET)
        {
            mGameBoard[0][1] = O;
            return;
        }
        if(mGameBoard[1][0] == UNSET)
        {
            mGameBoard[1][0] = O;
            return;
        }
        if(mGameBoard[2][1] == UNSET)
        {
            mGameBoard[2][1] = O;
            return;
        }
        if(mGameBoard[1][2] == UNSET)
        {
            mGameBoard[1][2] = O;
            return;
        }
    }

    if(mGameBoard[2][0] == X && mGameBoard[0][2] == X)
    {
        // play edge
        if(mGameBoard[0][1] == UNSET)
        {
            mGameBoard[0][1] = O;
            return;
        }
        if(mGameBoard[1][0] == UNSET)
        {
            mGameBoard[1][0] = O;
            return;
        }
        if(mGameBoard[2][1] == UNSET)
        {
            mGameBoard[2][1] = O;
            return;
        }
        if(mGameBoard[1][2] == UNSET)
        {
            mGameBoard[1][2] = O;
            return;
        }
    }

    // Hack to fix a fork.
    if(mGameBoard[1][0] == X && mGameBoard[0][1] == X)
    {
        if(mGameBoard[0][0] == UNSET)
        {
            mGameBoard[0][0] = O;
            return;
        }
    }

    // Play the center.
    if(mGameBoard[1][1] == GameBoard::UNSET)
    {
        mGameBoard[1][1] = GameBoard::O;
        return;
    }

    // Opposite Corner: If the opponent is in the corner, play the opposite
    //        corner.
    if(mGameBoard[0][0] == X)
        if(mGameBoard[2][2] == UNSET)
        {
            mGameBoard[2][2] = O;
            return;
        }
    if(mGameBoard[2][2] == X)
        if(mGameBoard[0][0] == UNSET)
        {
            mGameBoard[0][0] = O;
            return;
        }
    if(mGameBoard[0][2] == X)
        if(mGameBoard[2][0] == UNSET)
        {
            mGameBoard[2][0] = O;
            return;
        }
    if(mGameBoard[2][0] == X)
        if(mGameBoard[0][2] == UNSET)
        {
            mGameBoard[0][2] = O;
            return;
        }

    // Empty Corner: Play an empty corner.
    if (mGameBoard[2][2] == UNSET)
    {
        mGameBoard[2][2] = O;
        return;
    }
    if (mGameBoard[0][0] == UNSET)
    {
        mGameBoard[0][0] = O;
        return;
    }
    if (mGameBoard[2][0] == UNSET)
    {
        mGameBoard[2][0] = O;
        return;
    }
    if (mGameBoard[0][2] == UNSET)
    {
        mGameBoard[0][2] = O;
        return;
    }

    // For now if other case don't get used play random.
    randomAI();
}

bool GameBoard::threeInARow(Marker player)
{
    if ((mGameBoard[0][0] + mGameBoard[1][0] + mGameBoard[2][0]) == 2 * player)
    {
        if(mGameBoard[0][0] == UNSET) mGameBoard[0][0] = O;
        else if(mGameBoard[1][0] == UNSET) mGameBoard[1][0] = O;
        else if(mGameBoard[2][0] == UNSET) mGameBoard[2][0] = O;
        return true;
    }
    if ((mGameBoard[0][1] + mGameBoard[1][1] + mGameBoard[2][1]) == 2 * player)
    {
        if(mGameBoard[0][1] == UNSET) mGameBoard[0][1] = O;
        else if(mGameBoard[1][1] == UNSET) mGameBoard[1][1] = O;
        else if(mGameBoard[2][1] == UNSET) mGameBoard[2][1] = O;
        return true;
    }
    if ((mGameBoard[0][2] + mGameBoard[1][2] + mGameBoard[2][2]) == 2 * player)
    {
        if(mGameBoard[0][2] == UNSET) mGameBoard[0][2] = O;
        else if(mGameBoard[1][2] == UNSET) mGameBoard[1][2] = O;
        else if(mGameBoard[2][2] == UNSET) mGameBoard[2][2] = O;
        return true;
    }
    if ((mGameBoard[0][0] + mGameBoard[0][1] + mGameBoard[0][2]) == 2 * player)
    {
        if(mGameBoard[0][0] == UNSET) mGameBoard[0][0] = O;
        else if(mGameBoard[0][1] == UNSET) mGameBoard[0][1] = O;
        else if(mGameBoard[0][2] == UNSET) mGameBoard[0][2] = O;
        return true;
    }
    if ((mGameBoard[1][0] + mGameBoard[1][1] + mGameBoard[1][2]) == 2 * player)
    {
        if(mGameBoard[1][0] == UNSET) mGameBoard[1][0] = O;
        else if(mGameBoard[1][1] == UNSET) mGameBoard[1][1] = O;
        else if(mGameBoard[1][2] == UNSET) mGameBoard[1][2] = O;
        return true;
    }
    if ((mGameBoard[2][0] + mGameBoard[2][1] + mGameBoard[2][2]) == 2 * player)
    {
        if(mGameBoard[2][0] == UNSET) mGameBoard[2][0] = O;
        else if(mGameBoard[2][1] == UNSET) mGameBoard[2][1] = O;
        else if(mGameBoard[2][2] == UNSET) mGameBoard[2][2] = O;
        return true;
    }
    if ((mGameBoard[0][0] + mGameBoard[1][1] + mGameBoard[2][2]) == 2 * player)
    {
        if(mGameBoard[0][0] == UNSET) mGameBoard[0][0] = O;
        else if(mGameBoard[1][1] == UNSET) mGameBoard[1][1] = O;
        else if(mGameBoard[2][2] == UNSET) mGameBoard[2][2] = O;
        return true;
    }
    if ((mGameBoard[0][2] + mGameBoard[1][1] + mGameBoard[2][0]) == 2 * player)
    {
        if(mGameBoard[0][2] == UNSET) mGameBoard[0][2] = O;
        else if(mGameBoard[1][1] == UNSET) mGameBoard[1][1] = O;
        else if(mGameBoard[2][0] == UNSET) mGameBoard[2][0] = O;
        return true;
    }

    return false;
}

void GameBoard::randomAI()
{
    int i,j;
    while (true)
    {
        i = rand() % 3;
        j = rand() % 3;
        if (mGameBoard[i][j] == UNSET)
        {
            if (mHumanPlayer == X)
                mGameBoard[i][j] = O;
            else
                mGameBoard[i][j] = X;
            return;
        }
    }
}

bool GameBoard::isGameOver()
{
    int result = checkForWinner(X);
    if(result)
    {
        mWinner = result;
        return true;
    }

    result = checkForWinner(O);
    if(result)
    {
        mWinner = -result;
        return true;
    }

    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            if (mGameBoard[i][j] == GameBoard::UNSET)
                return false;

    return true;
}

int GameBoard::checkForWinner(Marker player)
{
    if(mGameBoard[0][0] == player && mGameBoard[1][0] == player &&
            mGameBoard[2][0] == player)
        return 1;
    else if(mGameBoard[0][1] == player && mGameBoard[1][1] == player &&
            mGameBoard[2][1] == player)
        return 2;
    else if(mGameBoard[0][2] == player && mGameBoard[1][2] == player &&
            mGameBoard[2][2] == player)
        return 3;
    else if(mGameBoard[0][0] == player && mGameBoard[0][1] == player &&
            mGameBoard[0][2] == player)
        return 4;
    else if(mGameBoard[1][0] == player && mGameBoard[1][1] == player &&
            mGameBoard[1][2] == player)
        return 5;
    else if(mGameBoard[2][0] == player && mGameBoard[2][1] == player &&
            mGameBoard[2][2] == player)
        return 6;
    else if(mGameBoard[0][0] == player && mGameBoard[1][1] == player &&
            mGameBoard[2][2] == player)
        return 7;
    else if(mGameBoard[0][2] == player && mGameBoard[1][1] == player &&
            mGameBoard[2][0] == player)
        return 8;
    else
        return 0;

}


int GameBoard::winner() const
{
    return mWinner;
}

